home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / Other Langs / Tickle-4.0 (tcl) / tcl / extend / src.unused / tclXselect.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-10-26  |  13.9 KB  |  438 lines  |  [TEXT/MPS ]

  1. /*
  2.  * tclXselect.c
  3.  *
  4.  * Extended Tcl file I/O commands.
  5.  *-----------------------------------------------------------------------------
  6.  * Copyright 1991-1993 Karl Lehenbauer and Mark Diekhans.
  7.  *
  8.  * Permission to use, copy, modify, and distribute this software and its
  9.  * documentation for any purpose and without fee is hereby granted, provided
  10.  * that the above copyright notice appear in all copies.  Karl Lehenbauer and
  11.  * Mark Diekhans make no representations about the suitability of this
  12.  * software for any purpose.  It is provided "as is" without express or
  13.  * implied warranty.
  14.  *-----------------------------------------------------------------------------
  15.  * $Id: tclXselect.c,v 2.7 1993/07/30 15:05:15 markd Exp $
  16.  *-----------------------------------------------------------------------------
  17.  */
  18.  
  19. #include "tclExtdInt.h"
  20.  
  21. #ifdef HAVE_SELECT
  22.  
  23. #ifdef HAVE_SYS_SELECT_H
  24. #   include <sys/select.h>
  25. #endif
  26.  
  27. extern
  28. double floor ();
  29.  
  30. /*
  31.  * A couple of systems (Xenix and older SCO unix) have bzero hidden away
  32.  * in the X library that we don't use, but the select macros use bzero.
  33.  * Make them use memset with this magic.
  34.  */
  35. #ifndef HAVE_BZERO
  36. #    define bzero(to,length)    memset(to,'\0',length)
  37. #endif
  38.  
  39. /*
  40.  * Macro to probe the stdio buffer to see if any data is pending in the
  41.  * buffer.  Different versions are provided for System V and BSD stdio.
  42.  */
  43.  
  44. #ifdef linux
  45. #   define READ_DATA_PENDING(fp) (fp->_egptr != fp->_gptr)
  46. #endif
  47. #if (!defined (READ_DATA_PENDING)) && defined __SLBF
  48. #   define READ_DATA_PENDING(fp) (fp->_r > 0)
  49. #endif
  50. #if !defined (READ_DATA_PENDING)
  51. #   define READ_DATA_PENDING(fp) (fp->_cnt != 0)
  52. #endif
  53.  
  54. /*
  55.  * A few systems (A/UX 2.0) have select but no macros, define em in this case.
  56.  */
  57. #ifndef FD_SET
  58. #   define FD_SET(fd,fdset)     (fdset)->fds_bits[0] |= (1<<(fd))
  59. #   define FD_CLR(fd,fdset)     (fdset)->fds_bits[0] &= ~(1<<(fd))
  60. #   define FD_ZERO(fdset)       (fdset)->fds_bits[0] = 0
  61. #   define FD_ISSET(fd,fdset)   (((fdset)->fds_bits[0]) & (1<<(fd)))
  62. #endif
  63.  
  64. /*
  65.  * Prototypes of internal functions.
  66.  */
  67. static int
  68. ParseSelectFileList _ANSI_ARGS_((Tcl_Interp *interp,
  69.                                  char       *handleList,
  70.                                  fd_set     *fileDescSetPtr,
  71.                                  FILE     ***fileDescListPtr,
  72.                                  int        *maxFileIdPtr));
  73.  
  74. static int
  75. FindPendingData _ANSI_ARGS_((int         fileDescCnt,
  76.                              FILE      **fileDescList,
  77.                              fd_set     *fileDescSetPtr));
  78.  
  79. static char *
  80. ReturnSelectedFileList _ANSI_ARGS_((fd_set     *fileDescSetPtr,
  81.                                     fd_set     *fileDescSet2Ptr,
  82.                                     int         fileDescCnt,
  83.                                     FILE      **fileDescList));
  84.  
  85.  
  86. /*
  87.  *-----------------------------------------------------------------------------
  88.  *
  89.  * ParseSelectFileList --
  90.  *
  91.  *   Parse a list of file handles for select.
  92.  *
  93.  * Parameters:
  94.  *   o interp (O) - Error messages are returned in the result.
  95.  *   o handleList (I) - The list of file handles to parse, may be empty.
  96.  *   o fileDescSetPtr (O) - The select fd_set for the parsed handles is
  97.  *     filled in.  Should be cleared before this procedure is called.
  98.  *   o fileDescListPtr (O) - A pointer to a dynamically allocated list of
  99.  *     the FILE ptrs that are in the set.  If the list is empty, NULL is
  100.  *     returned.
  101.  *   o maxFileIdPtr (I/O) - If a file id greater than the current value is
  102.  *     encountered, it will be set to that file id.
  103.  * Returns:
  104.  *   The number of files in the list, or -1 if an error occured.
  105.  *-----------------------------------------------------------------------------
  106.  */
  107. static int
  108. ParseSelectFileList (interp, handleList, fileDescSetPtr, fileDescListPtr,
  109.                      maxFileIdPtr)
  110.     Tcl_Interp *interp;
  111.     char       *handleList;
  112.     fd_set     *fileDescSetPtr;
  113.     FILE     ***fileDescListPtr;
  114.     int        *maxFileIdPtr;
  115. {
  116.     int    handleCnt, idx;
  117.     char **handleArgv;
  118.     FILE **fileDescList;
  119.  
  120.     /*
  121.      * Optimize empty list handling.
  122.      */
  123.     if (handleList [0] == '\0') {
  124.         *fileDescListPtr = NULL;
  125.         return 0;
  126.     }
  127.  
  128.     if (Tcl_SplitList (interp, handleList, &handleCnt, &handleArgv) != TCL_OK)
  129.         return -1;
  130.  
  131.     /*
  132.      * Handle case of an empty list.
  133.      */
  134.     if (handleCnt == 0) {
  135.         *fileDescListPtr = NULL;
  136.         ckfree ((char *) handleArgv);
  137.         return 0;
  138.     }
  139.  
  140.     fileDescList = (FILE **) ckalloc (sizeof (FILE *) * handleCnt);
  141.  
  142.     for (idx = 0; idx < handleCnt; idx++) {
  143.         FILE *filePtr;
  144.         int   fileId;
  145.  
  146.         if (Tcl_GetOpenFile (interp, handleArgv [idx],
  147.                              FALSE, FALSE,  /* No checking */
  148.                              &filePtr) != TCL_OK) {
  149.             ckfree ((char *) handleArgv);
  150.             ckfree ((char *) fileDescList);
  151.             return -1;
  152.         }
  153.         fileId = fileno (filePtr);
  154.         fileDescList [idx] = filePtr;
  155.  
  156.         FD_SET (fileId, fileDescSetPtr);
  157.         if (fileId > *maxFileIdPtr)
  158.             *maxFileIdPtr = fileId;
  159.     }
  160.  
  161.     *fileDescListPtr = fileDescList;
  162.     ckfree ((char *) handleArgv);
  163.     return handleCnt;
  164. }
  165.  
  166. /*
  167.  *-----------------------------------------------------------------------------
  168.  *
  169.  * FindPendingData --
  170.  *
  171.  *   Scan a list of read file descriptors to determine if any of them
  172.  *   have data pending in their stdio buffers.
  173.  *
  174.  * Parameters:
  175.  *   o fileDescCnt (I) - Number of descriptors in the list.
  176.  *   o fileDescListPtr (I) - A pointer to a list of the FILE pointers for
  177.  *     files that are in the set.
  178.  *   o fileDescSetPtr (I) - A select fd_set with will have a bit set for
  179.  *     every file that has data pending it its buffer.
  180.  * Returns:
  181.  *   TRUE if any where found that had pending data, FALSE if none were found.
  182.  *-----------------------------------------------------------------------------
  183.  */
  184. static int
  185. FindPendingData (fileDescCnt, fileDescList, fileDescSetPtr)
  186.     int         fileDescCnt;
  187.     FILE      **fileDescList;
  188.     fd_set     *fileDescSetPtr;
  189. {
  190.     int idx, found = FALSE;
  191.  
  192.     FD_ZERO (fileDescSetPtr);
  193.  
  194.     for (idx = 0; idx < fileDescCnt; idx++) {
  195.         if (READ_DATA_PENDING (fileDescList [idx])) {
  196.             FD_SET (fileno (fileDescList [idx]), fileDescSetPtr);
  197.             found = TRUE;
  198.         }
  199.     }
  200.     return found;
  201. }
  202.  
  203. /*
  204.  *-----------------------------------------------------------------------------
  205.  *
  206.  * ReturnSelectedFileList --
  207.  *
  208.  *   Take the resulting file descriptor sets from a select, and the
  209.  *   list of file descritpors and build up a list of Tcl file handles.
  210.  *
  211.  * Parameters:
  212.  *   o fileDescSetPtr (I) - The select fd_set.
  213.  *   o fileDescSet2Ptr (I) - Pointer to a second descriptor to also check
  214.  *     (their may be overlap).  NULL if no second set.
  215.  *   o fileDescCnt (I) - Number of descriptors in the list.
  216.  *   o fileDescListPtr (I) - A pointer to a list of the FILE pointers for
  217.  *     files that are in the set.  If the list is empty, NULL is returned.
  218.  * Returns:
  219.  *   A dynamicly allocated list of file handles.  If the handles are empty,
  220.  *   it still returns a NULL list to make clean up easy.
  221.  *-----------------------------------------------------------------------------
  222.  */
  223. static char *
  224. ReturnSelectedFileList (fileDescSetPtr, fileDescSet2Ptr, fileDescCnt,
  225.                         fileDescList) 
  226.     fd_set     *fileDescSetPtr;
  227.     fd_set     *fileDescSet2Ptr;
  228.     int         fileDescCnt;
  229.     FILE      **fileDescList;
  230. {
  231.     int    idx, handleCnt, fileNum;
  232.     char  *fileHandleList;
  233.     char **fileHandleArgv, *nextByte;
  234.  
  235.     /*
  236.      * Special case the empty list.
  237.      */
  238.     if (fileDescCnt == 0) {
  239.         fileHandleList = ckalloc (1);
  240.         fileHandleList [0] = '\0';
  241.         return fileHandleList;
  242.     }
  243.  
  244.     /*
  245.      * Allocate enough room to hold the argv plus all the `fileNNN' strings
  246.      */
  247.     fileHandleArgv = (char **)
  248.         ckalloc ((fileDescCnt * sizeof (char *)) + (9 * fileDescCnt));
  249.     nextByte = ((char *) fileHandleArgv) + (fileDescCnt * sizeof (char *));
  250.  
  251.     handleCnt = 0;
  252.     for (idx = 0; idx < fileDescCnt; idx++) {
  253.         fileNum = fileno (fileDescList [idx]);
  254.  
  255.         if (FD_ISSET (fileNum, fileDescSetPtr) ||
  256.             (fileDescSet2Ptr != NULL &&
  257.              FD_ISSET (fileNum, fileDescSet2Ptr))) {
  258.  
  259.             fileHandleArgv [handleCnt] = nextByte;  /* Allocate storage */
  260.             nextByte += 8;
  261.             sprintf (fileHandleArgv [handleCnt], "file%d", fileNum);
  262.             handleCnt++;
  263.         }
  264.     }
  265.  
  266.     fileHandleList = Tcl_Merge (handleCnt, fileHandleArgv);
  267.     ckfree ((char *) fileHandleArgv);
  268.  
  269.     return fileHandleList;
  270. }
  271.  
  272. /*
  273.  *-----------------------------------------------------------------------------
  274.  *
  275.  * Tcl_SelectCmd --
  276.  *  Implements the select TCL command:
  277.  *      select readhandles ?writehandles? ?excepthandles? ?timeout?
  278.  *
  279.  *  This command is extra smart in the fact that it checks for read data
  280.  * pending in the stdio buffer first before doing a select.
  281.  *   
  282.  * Results:
  283.  *     A list in the form:
  284.  *        {readhandles writehandles excepthandles}
  285.  *     or {} it the timeout expired.
  286.  *-----------------------------------------------------------------------------
  287.  */
  288. int
  289. Tcl_SelectCmd (clientData, interp, argc, argv)
  290.     ClientData  clientData;
  291.     Tcl_Interp *interp;
  292.     int         argc;
  293.     char      **argv;
  294. {
  295.  
  296.     fd_set readFdSet,            writeFdSet,            exceptFdSet;
  297.     int    readDescCnt = 0,      writeDescCnt = 0,      exceptDescCnt = 0;
  298.     FILE **readDescList = NULL,**writeDescList = NULL,**exceptDescList = NULL;
  299.     fd_set readFdSet2;
  300.     char  *retListArgv [3];
  301.  
  302.     int             numSelected, maxFileId = 0, pending;
  303.     int             result = TCL_ERROR;
  304.     struct timeval  timeoutRec;
  305.     struct timeval *timeoutRecPtr;
  306.  
  307.  
  308.     if (argc < 2) {
  309.         Tcl_AppendResult (interp, tclXWrongArgs, argv [0], 
  310.                           " readFileIds ?writeFileIds? ?exceptFileIds?",
  311.                           " ?timeout?", (char *) NULL);
  312.         return TCL_ERROR;
  313.     }
  314.     
  315.     /*
  316.      * Parse the file handles and set everything up for the select call.
  317.      */
  318.     FD_ZERO (&readFdSet);
  319.     FD_ZERO (&writeFdSet);
  320.     FD_ZERO (&exceptFdSet);
  321.     readDescCnt = ParseSelectFileList (interp, argv [1], &readFdSet, 
  322.                                        &readDescList, &maxFileId);
  323.     if (readDescCnt < 0)
  324.         goto exitPoint;
  325.     if (argc > 2) {
  326.         writeDescCnt = ParseSelectFileList (interp, argv [2], &writeFdSet, 
  327.                                             &writeDescList, &maxFileId);
  328.         if (writeDescCnt < 0)
  329.             goto exitPoint;
  330.     }
  331.     if (argc > 3) {
  332.         exceptDescCnt = ParseSelectFileList (interp, argv [3], &exceptFdSet, 
  333.                                              &exceptDescList, &maxFileId);
  334.         if (exceptDescCnt < 0)
  335.             goto exitPoint;
  336.     }
  337.     
  338.     /*
  339.      * Get the time out.  Zero is different that not specified.
  340.      */
  341.     timeoutRecPtr = NULL;
  342.     if ((argc > 4) && (argv [4][0] != '\0')) {
  343.         double  timeout, seconds, microseconds;
  344.  
  345.         if (Tcl_GetDouble (interp, argv [4], &timeout) != TCL_OK)
  346.             goto exitPoint;
  347.         if (timeout < 0) {
  348.             Tcl_AppendResult (interp, "timeout must be greater than or equal",
  349.                               " to zero", (char *) NULL);
  350.             goto exitPoint;
  351.         }
  352.         seconds = floor (timeout);
  353.         microseconds = (timeout - seconds) * 1000000.0;
  354.         timeoutRec.tv_sec = seconds;
  355.         timeoutRec.tv_usec = microseconds;
  356.         timeoutRecPtr = &timeoutRec;
  357.     }
  358.  
  359.     /*
  360.      * Check if any data is pending in the read stdio buffers.  If there is,
  361.      * then do the select, but don't block in it.
  362.      */
  363.  
  364.     pending = FindPendingData (readDescCnt, readDescList, &readFdSet2);
  365.     if (pending) {
  366.         timeoutRec.tv_sec = 0;
  367.         timeoutRec.tv_usec = 0;
  368.         timeoutRecPtr = &timeoutRec;
  369.     }
  370.  
  371.     /*
  372.      * All set, do the select.
  373.      */
  374.     numSelected = select (maxFileId + 1, &readFdSet, &writeFdSet, &exceptFdSet,
  375.                           timeoutRecPtr);
  376.     if (numSelected < 0) {
  377.         interp->result = Tcl_PosixError (interp);
  378.         goto exitPoint;
  379.     }
  380.  
  381.     /*
  382.      * Return the result, either a 3 element list, or leave the result
  383.      * empty if the timeout occured.
  384.      */
  385.     if (numSelected > 0 || pending) {
  386.         retListArgv [0] = ReturnSelectedFileList (&readFdSet,
  387.                                                   &readFdSet2,
  388.                                                   readDescCnt,
  389.                                                   readDescList);
  390.         retListArgv [1] = ReturnSelectedFileList (&writeFdSet,
  391.                                                   NULL,
  392.                                                   writeDescCnt, 
  393.                                                   writeDescList);
  394.         retListArgv [2] = ReturnSelectedFileList (&exceptFdSet,
  395.                                                   NULL,
  396.                                                   exceptDescCnt, 
  397.                                                   exceptDescList);
  398.         Tcl_SetResult (interp, Tcl_Merge (3, retListArgv), TCL_DYNAMIC); 
  399.         ckfree ((char *) retListArgv [0]);
  400.         ckfree ((char *) retListArgv [1]);
  401.         ckfree ((char *) retListArgv [2]);
  402.     }
  403.  
  404.     result = TCL_OK;
  405.  
  406. exitPoint:
  407.     if (readDescList != NULL)
  408.         ckfree ((char *) readDescList);
  409.     if (writeDescList != NULL)
  410.         ckfree ((char *) writeDescList);
  411.     if (exceptDescList != NULL)
  412.         ckfree ((char *) exceptDescList);
  413.     return result;
  414.  
  415. }
  416. #else
  417. /*
  418.  *-----------------------------------------------------------------------------
  419.  *
  420.  * Tcl_SelectCmd --
  421.  *     Dummy select command that returns an error for systems that don't
  422.  *     have select.
  423.  *-----------------------------------------------------------------------------
  424.  */
  425. int
  426. Tcl_SelectCmd (clientData, interp, argc, argv)
  427.     ClientData  clientData;
  428.     Tcl_Interp *interp;
  429.     int         argc;
  430.     char      **argv;
  431. {
  432.     Tcl_AppendResult (interp, 
  433.                       "select is not available on this version of Unix",
  434.                       (char *) NULL);
  435.     return TCL_ERROR;
  436. }
  437. #endif
  438.